Skip to content

Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic headline#116

Draft
davorrunje wants to merge 18 commits into
mainfrom
spec/applications-structure-preserving-pinns
Draft

Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic headline#116
davorrunje wants to merge 18 commits into
mainfrom
spec/applications-structure-preserving-pinns

Conversation

@davorrunje

Copy link
Copy Markdown
Owner

Applications: Structure-Preserving PINNs (Paper 1) — framework + inverse/traffic study

New top-level applications/ area (research papers built on mononet, out of the wheel) and its first occupant, applications/pinn: structure-preserving PINNs, where mononet's expressive hard monotonicity enforces a PDE solution's admissibility (TVD/entropy) by construction instead of via soft penalties.

Draft — headline (both tiers), the residual-vs-plain field ablation, and the high-noise robustness sweep (both fields) are in. Pending: figures, a non-PINN/smoothness-prior baseline (being added), cross-backend check.

Central result: robustness under noise (two PDEs, sparsity × noise, 10-seed IQM)

Inverse reconstruction from sparse noisy observations, on LWR (concave flux) and Burgers (convex flux).

Whole-field accuracy (L¹) — the general result: from noise ≥ 0.05, hard-monotone has the lowest L¹ error in 16/16 noisy LWR cells and 15/16 noisy Burgers cells (all observation counts), margin widening with noise (n_obs=80/0.20: LWR 13.2 vs 17.0/21.5; Burgers 19.0 vs 20.6/29.3). And it is exactly admissible (violation 0) at every one of the 40 cells vs baselines oscillating to 0.9–1.3. The monotone prior regularizes the noise the baselines over-fit everywhere.

Front-weighted accuracy (L²) is PDE-dependent: clean on LWR (hard best from 0.05, e.g. 0.20: 1.39 vs 1.59/1.97) but mixed on Burgers — its steeper shock lets the oscillating baselines match/beat hard on L² at high noise (0.20: 1.90 vs 1.82, while they violate 0.86). L² is dominated by the sharp front, where a continuous monotone ramp can't beat a steep (oscillating) fit; L¹ integrates the whole field, where the prior wins broadly.

Honest summary: robust general claims are (i) lower whole-field (L¹) error under noise on both PDEs and (ii) guaranteed admissibility everywhere; the "beats on every metric" reading holds cleanly only for LWR (Burgers L² is a wash-to-slight-loss at the highest noise). We report both norms rather than lead with the flattering one.

Headline panels (MonoResidual, 10-seed IQM, equal-budget tuned)

Inverse flagship (noise 0.02): hard-monotone L¹ 3.52 / L² 0.714 / viol 0 — best + only admissible; vanilla 3.92/0.731/0.083; soft 3.95/0.723/0.104; weight_clip 22.5/2.10 (fails → the effect is expressiveness, not monotonicity per se). Forward tier (worst case): constraint pays an accuracy cost (L¹ 8.1 vs 6.1), stays oscillation-free — a mechanism check.

Result strength & relevance (honest assessment)

Strong:

  • General & robust: crossover across 4 sparsities × 5 noise levels × both fields (20 cells, monotone trend) — a real mechanism, not a single-point fluke.
  • Rigorous: equal-budget Optuna per method (baselines not under-tuned), 10-seed IQM, tuned-once/stress-tested, fully reproducible (artifacts + runners + RUNBOOK).
  • Interpretable + controlled: prior helps exactly when data is scarce/noisy; the soft baseline (tuned monotonicity penalty) still loses and the inexpressive weight_clip fails → it is specifically expressive hard monotonicity.
  • Dual benefit: lower error and guaranteed admissibility — the latter has independent value for trust/safety that L² undersells.
  • Non-PINN comparator (new): a tuned classical RBF smoother (no PDE, no monotonicity) is ~2× worse across all noise (n_obs=80: 1.37→2.19 vs 0.70→1.26) and inadmissible — so the win needs both the PDE and expressive hard monotonicity; generic smoothing reproduces neither.

Threats to validity / scope:

  • 1-D, single PDE (LWR), monotone-solution class — the prior is known-true here by construction; no evidence yet where monotonicity is only approximately right, or in higher dimensions.
  • Synthetic i.i.d. Gaussian noise favors a smoothing prior; real sensor noise is biased/correlated/MNAR.
  • Monotonicity-vs-generic-smoothness (within-PINN) not fully separated — a curvature-penalty PINN would isolate it given the PDE. (A non-PINN smoothness comparator is now included — see below.)
  • Magnitude moderate (~20–36% at high noise); the sweep reports IQM (robustness comes from the cross-cell trend, not per-cell CIs).

Relevance: high for the paper's thesis (turns "admissible but not more accurate" into "better accuracy + guaranteed structure, growing with noise"); moderate for the broader field, bounded by the 1-D/single-PDE/monotone-class scope. Lift items, in priority: (1) a 2nd PDE or higher-D, (2) a real noisy dataset, (3) a within-PINN curvature-penalty control.

What's here

Framework (admissibility abstraction, problem registry, NumPy core/, JAX+PyTorch builders/trainers with grad-clip + input normalization, tier-aware Optuna search, resume-able IQM runner/sweep); field ablation as a config knob; two tiers.

Upstream fix (#100)

MonoResidual had collapsed to a near-linear map; PR #100 fixes it (merged). Both backends use MonoResidual + input normalization.

Testing

uv run pytest tests/applications → 81 passed (Torch tests skip in gpu-jax); ruff + mypy clean (203 files); docs build passes.

Known gaps (RUNBOOK/FINDINGS)

Figures (crossover curve + reconstructed field); cross-backend equivalence (needs Torch → default); physical-bounds [0, ρ_max] metric.

Figures

Eight figures in paper/figures/, embedded in §6.2.1, regenerated by
python -m applications.pinn.experiments.figures:

  • Reconstruction slices (both PDEs, stress point n_obs=80/noise=0.20): the money-shot — hard-monotone is a clean in-range monotone ramp while vanilla/soft overshoot and dip below zero (unphysical).
  • Formats: each figure ships as vector PDF (for LaTeX \includegraphics, fonts embedded Type-42; via Git LFS) and PNG (for this markdown/Sphinx preview).
  • Crossover curves (L¹/L²/admissibility vs noise): comparable at zero noise, hard-monotone L¹ separates cleanly below both baselines as noise grows; admissibility stays flat at 0.

Also merged latest origin/main (legacy port, Stage-2 benchmark, gate fix now on main); one test conflict resolved, branch is MERGEABLE.

NGSIM real-data validation (Paper 1 §6.5 scaffolding)

Adds a real-traffic-data (NGSIM I-80) inverse-reconstruction path to attack the synthetic-noise caveat of the results above. Built TDD across 10 tasks (spec: docs/superpowers/specs/2026-07-13-real-traffic-data-ngsim-design.md, plan: docs/superpowers/plans/2026-07-13-real-traffic-data-ngsim.md); CI-inert (synthetic fixtures only — no raw NGSIM needed to build/test):

  • applications/pinn/data/ngsim.py — Edie aggregation (trajectories → dense ρ(x,t)), objective monotone-window scan (reuses admissibility.violation, reports monotonicity_defect), Greenshields FD calibration, CLI → derived .npz (Git LFS; raw gitignored).
  • core/problems/traffic_real.pyngsim_wave Problem loading the .npz (lazy scipy; ground_truth = bilinear interp of the Edie field; smooth Greenshields f' for the residual).
  • core/sampling.py — virtual loop-detector observation mode + disjoint held-out detectors.
  • run.py/config.py/headline.py--observations detectors + held-out-detector RMSE metric.
  • baselines.py — Adaptive Smoothing Method (Treiber–Helbing) TSE baseline.
  • figures.py --real — reconstruction slice + window heatmap + metric bars, computing ASM + RBF inline (PDF+PNG).
  • RUNBOOK.md — reproduction commands, exploratory-gate note, ASM-tuning caveat.

Metrics mirror the synthetic sections (L¹/L²/admissibility) plus held-out-detector RMSE; monotonicity holds by disclosed window selection (single stop-and-go wave), with the reference's residual defect reported.

Remaining (human-in-the-loop, needs the raw NGSIM file): drop raw I-80 → preprocess → pass the exploratory gate → tune ASM → run headline + figures → write §6.5. The ASM baseline parameterization is deliberately deferred to that gate (documented in the RUNBOOK).


Fresh draft superseding the closed #98 (reopen was blocked after the main LFS history rewrite; branch content unchanged).

davorrunje and others added 18 commits July 12, 2026 07:13
… (Paper 1)

Recreated in the devcontainer workspace (the original branch lived only in a
separate host checkout at ~/projects/PhD/pinn/mononet, never pushed; this
container binds ~/projects/PhD/mononet). Consolidated final content:
applications/ area + Paper 1 (framework + inverse conservation/traffic
flagship), forward mechanism tier, HJB->Paper 2 with de-risking caveats,
Optuna equal-budget, shallow MonoResidual, gpu-jax compute strategy,
manuscript-first sequencing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New top-level applications/ area (repo-only, out of the wheel) for research
papers built on mononet, distinct from benchmarks/. Adds:
- applications/_common: seeding, metrics_io, plot_theme (framework-free)
- applications/pinn package skeleton (core, models/{jax,torch}, training,
  experiments, configs, results, notebooks, paper, studies) + README/RUNBOOK
- lazy-import test: importing applications.pinn pulls in no torch/jax
- wire applications/ into mypy files + ruff include (not into --cov, like
  benchmarks)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Manuscript-first per spec §11: paper/paper.md written end-to-end (abstract,
intro, related work, method, theory, experimental design, discussion) with
numerical results as explicit [[…]] placeholders, each backed by a planned
Phase 5/6 experiment. references.bib gathers the load-bearing citations;
uncertain 2026 entries marked note={VERIFY}. No code yet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- core/admissibility.py: AdmissibilitySpec (mask + convex/concave axes) and
  violation() — non-negative wrong-sign spatial-variation metric (0 iff monotone
  in the required direction); the paper's headline metric.
- core/problems/base.py: Problem protocol + register/get/available registry.
- Tests cover violation semantics (bump rise, symmetry, unconstrained axis) and
  registry roundtrip / unknown / duplicate / sorted.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
core/exact.py: exact Burgers Riemann (shock at Rankine-Hugoniot speed +
rarefaction), linear advection, LWR/Greenshields Riemann (shock + rarefaction,
concave flux), breaking_time, and pre-shock characteristic solve. Tests check
shock speeds, self-similar rarefactions, monotonicity, the t=0 step, and the
implicit characteristic relation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
core/reference_solver.py: exact Godunov interface flux (convex Burgers and
concave LWR via the sonic point) with CFL-limited explicit stepping and outflow
BCs. Tests: L1 convergence to exact Burgers/LWR Riemann under refinement,
correct shock speed within a cell, and TV non-increasing (TVD).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(Phase 2.5)

core/sampling.py: seeded collocation (uniform / Latin-hypercube), initial and
boundary points, structured eval grid, and observations() — sparse, noisy
draws from a reference field for the inverse flagship. Tests cover determinism,
in-domain bounds, boundary placement, and noiseless/noisy observation recovery.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
BurgersRiemann, LinearAdvection, LwrRiemann (closed-form ground truth) and
BurgersSmoothShock (characteristic pre-breaking, cached Godunov reference
after) — each with flux, sonic point, and admissibility mask matching the
entropy solution's monotone direction. problems/__init__ registers them on
import. Add reference_solver.interpolate (bilinear). Registry decorator made
generic so plugins register without compile-time Problem conformance.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…hase 2.7)

core/metrics.py: L1/L2, TV and TV(t) curve, Gibbs overshoot, shock-position
error, mass error, field reconstruction error. core/plotting.py: pure
object-oriented matplotlib figures (profiles, TV curves, field heatmap), no
pyplot/global state. Adds a subprocess test asserting the whole core imports
neither torch nor jax (Phase 2 checkpoint invariant).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 3.1 torch)

models/protocol.py: ModelConfig + build() dispatcher. models/torch/builders.py:
VanillaMLP (also the soft architecture), WeightClipMono (inexpressive
non-negative-weight baseline), HardMonoField (expressive, from mononet layers).
Monotone-in-x / free-in-t via an unconstrained t-embedding fed with [x,h(t)]
to a stack monotone in all inputs (mask [sign_x, +1...]). Tests verify
by-construction monotonicity (both signs), weight-clip monotonicity, shapes,
and t-dependence. JAX builders + cross-backend equivalence pending a
both-backends environment.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…luxes

Application deps move out of the shared `dev` group into opt-in groups mirroring
`bench`: `applications` (shared, matplotlib) and `pinn` (includes applications +
optax). Backends stay package extras; `dev` stays lean. Work on the app with
`uv sync --extra jax --group pinn`. Application tests importorskip matplotlib/
optax and the backend, so the dev-only suite skips them cleanly (plotting test
split into its own importorskip-guarded file).

Make Problem.flux/flux_prime backend-polymorphic (drop np.asarray/np.full_like)
so the PDE residual differentiates through them under jax/torch tracing;
verified jax.grad(flux) == flux_prime for all three problems.

Independent per-application uv projects (own lockfile/venv) recorded as the
agreed follow-up to prevent cross-app version conflicts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Flax NNX VanillaMLP / WeightClipMono / HardMonoField mirroring the torch
builders; protocol.build() dispatches to jax; ModelConfig.mode typed as Mode;
blocks stored as nnx-tracked attributes; nnx.Param via get_value(). Tests
importorskip jax/flax.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
training/losses.py: backend-agnostic LossWeights + TrainingData (collocation,
IC/BC, sparse observations). training/jax_trainer.py: optax Adam loop with the
PDE residual u_t + f'(u) u_x built via jax.grad input derivatives over an
nnx.split pure-params function (first-order, no Hessian); residual/IC/BC/data
terms + optional soft monotonicity penalty (soft baseline only). Smoke tests:
loss decreases, hard-monotone field stays admissible (violation<1e-5) after
training, vanilla trains too. importorskip jax/flax/optax.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…3.3 torch)

training/torch_trainer.py: Adam loop with the PDE residual via
autograd.grad(create_graph=True), mirroring the JAX trainer (residual/IC/BC/
data + optional soft monotonicity penalty). Smoke tests: loss decreases,
hard-monotone stays admissible after training, vanilla trains.

tests/.../models/test_cross_backend.py: application-level cross-backend parity
(both backends satisfy the by-construction monotonicity on identical inputs).
Bitwise kernel equivalence is covered upstream by tests/equivalence/; trained-run
numeric agreement is an empirical Phase-6 result.

Closes Phase 3 (models + trainers, both backends).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
experiments/config.py: RunConfig fully specifies one (problem,method,backend,
seed) run. experiments/run.py: run_one() builds model -> assembles forward-tier
training data -> trains (backend dispatch) -> predicts on the eval grid ->
scores vs ground truth (l1/l2, admissibility violation, overshoot); argparse
CLI. Add optuna to the pinn group and a T201 ignore for experiment CLIs.
Smoke tests: valid artifact, hard-monotone violation<1e-4, soft baseline runs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…2/4.3)

experiments/search.py: Optuna TPE search with an identical space + trial budget
across methods (soft additionally searches its penalty weight, per the §6a
fair-comparison rule); objective = L2 vs ground truth; best configs frozen to
configs/ and reloadable. experiments/sweep.py: build_matrix over
problem x method x backend x seed and run_matrix composing run_one. Smoke tests
cover search/freeze/reload, soft-penalty inclusion, and a 2-cell sweep.

Closes Phase 4. Phases 5-6 (result runs) remain and need real compute + GPU.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
davorrunje added a commit that referenced this pull request Jul 15, 2026
…try)

Add a Multi-paper scoping section to all three skill specs: skills operate on a
research project selected by paper-id via a docs/research/papers.md registry; paths are
relative to the resolved paper root (main paper at docs/research/main/, application
papers at applications/<slug>/ per the PR #116 convention). Application papers read the
main paper read-only. Example paths updated to the main-paper root.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant